#pragma once

#include <vector>
#include <map>
#include <queue>
using namespace std;

class IGameState
{

public:

	virtual void OnEnter(const char* previousState) = 0;

	virtual void OnExit(const char* nextState) = 0;

	virtual void OnOverride(const char* nextState) = 0;

	virtual void OnResume(const char* previousState) = 0;

	virtual void Input() = 0;

	virtual void Update() = 0;

	virtual void Render() = 0;

	virtual const char* GetName() = 0;

};

class WhyStateSystem
{
	enum GameStateCmdType
	{
		CMD_PUSH,
		CMD_CHANGE,
		CMD_POP,
		CMD_POPALL,
		CMD_FIRST
	};

	struct GameStateCmd
	{
		GameStateCmdType cmd;
		const char* stateName;
		float delay;

		GameStateCmd()
		{
			cmd = CMD_FIRST;
			stateName = NULL;
			delay = 0.0f;
		}
	};

	vector<IGameState*> stateStack;
	map<const char*, IGameState*> registeredStates;
	queue<GameStateCmd> cmdQueue;
	float currenttime;

	void SendCmd(GameStateCmdType cmdType, const char* stateName, float delay = 0.0f);

public:

	void RegisterState(IGameState* state);

	void ChangeState(const char* stateName, float delay = 0.0f, bool shouldFlush = false);

	void PushState(const char* stateName, float delay = 0.0f, bool shouldFlush = false);

	void PopState(int numStatesToPop = 1, float delay = 0.0f, bool shouldFlush = false);

	void PopAllStates(float delay = 0.0f, bool shouldFlush = false);

	void ProcessCmdQueue();

	void Update(float deltatime);

	void Render();

	void ShutDown();

	bool IsStateChangePending() { return (cmdQueue.empty()) ? false : true; }

	const char* GetCurrentStateName() { return GetCurrentState()->GetName(); }

	IGameState* GetState(const char* stateName) { return registeredStates[stateName]; }

	IGameState* GetCurrentState() { return stateStack[stateStack.size() - 1]; }
};